using Devil;
using UnityEngine;

namespace GameToolkit
{
    public interface IMutexComponent
    {
        bool IsActiveComponent { get; }
    }

    /// <summary>
    /// 优先级定义的互斥组件（在同一时间，仅一个组件处于活跃状态）
    /// </summary>
    /// <typeparam name="T"></typeparam>
    public abstract class MutexComponent<T> : MonoBehaviour, IMutexComponent where T : MutexComponent<T>
    {
        public static event System.Action<T> OnActiveComponentChanged;

        public static T ActiveComponent => sComponents.Count == 0 ? null : sComponents.TopValule;

        readonly static MinHeap<T> sComponents = new MinHeap<T>(8, (a, b) => a.mPriority < b.mPriority ? 1 : (a.mPriority > b.mPriority ? -1 : 0));

        [SerializeField]
        int m_Priority;

        int mPriority;
        public int Priority => mPriority;

        /// <summary>
        /// 当前是否为活跃对象
        /// </summary>
        public bool IsActiveComponent => sComponents.Count > 0 && sComponents.TopValule == this;

        protected virtual void OnBecomeDeactive(T nextComponent) { }
        protected virtual void OnBecomeActive(T prevComponent) { }

        protected virtual void OnEnable()
        {
            mPriority = m_Priority;
            var top = sComponents.Count > 0 ? sComponents.TopValule : null;
            sComponents.Add((T)this);
            var current = sComponents.TopValule;
            if (current != top)
            {
                try
                {
                    if (top != null)
                        top.OnBecomeDeactive(current);
                    current.OnBecomeActive(top);
                    OnActiveComponentChanged?.Invoke(current);
                }
                catch (System.Exception e)
                {
                    Debug.LogException(e);
                }
            }
        }

        protected virtual void OnDisable()
        {
            var top = sComponents.Count > 0 ? sComponents.TopValule : null;
            sComponents.Remove((T)this);
            var current = sComponents.Count > 0 ? sComponents.TopValule : null;
            if (current != top)
            {
                try
                {
                    if (top != null)
                        top.OnBecomeDeactive(current);
                    if (current != null)
                        current.OnBecomeActive(top);
                    OnActiveComponentChanged?.Invoke(current);
                }
                catch (System.Exception e)
                {
                    Debug.LogException(e);
                }
            }
        }

        protected virtual void OnValidate()
        {
            if (mPriority != m_Priority && sComponents.Contains((T)this))
            {
                var top = sComponents.Count > 0 ? sComponents.TopValule : null;
                sComponents.Remove((T)this);
                mPriority = m_Priority;
                sComponents.Add((T)this);
                var current = sComponents.TopValule;
                if (current != top)
                {
                    try
                    {
                        if (top != null)
                            top.OnBecomeDeactive(current);
                        current.OnBecomeActive(top);
                        OnActiveComponentChanged?.Invoke(current);
                    }
                    catch (System.Exception e)
                    {
                        Debug.LogException(e);
                    }
                }
            }
        }
    }
}
